﻿module SeqExtensions
open TupleExtensions2
open System.Collections.Generic

module EnumeratorFunctionAsTypes =
    type GetEnumerator = GetEnumerator with static member ($) (GetEnumerator, x:seq<_>        ) = x.GetEnumerator()
    type MoveNext      = MoveNext      with static member ($) (MoveNext     , x:IEnumerator<_>) = x.MoveNext()
    type Current       = Current       with static member ($) (Current      , x:IEnumerator<_>) = x.Current
    type DisposeEnum   = DisposeEnum   with static member ($) (DisposeEnum  , x:IEnumerator<_>) = x.Dispose()

module Seq =
    open EnumeratorFunctionAsTypes
    let inline zipMany (seqs) =
        let enums = Tuple.mapType GetEnumerator seqs
        seq {
            while (Tuple.mapType MoveNext enums |> Tuple.toList |> List.forall id) do
            yield Tuple.mapType Current enums
            }

module PairSeq =
    module PairSeqInternalValues =
        type CurrentKey   = CurrentKey   with static member ($) (CurrentKey  , x:IEnumerator<_>) = fst x.Current
        type CurrentValue = CurrentValue with static member ($) (CurrentValue, x:IEnumerator<_>) = snd x.Current
        type SomeKeys     = SomeKeys     with static member ($) (SomeKeys    , x:IEnumerator<_>) = fun s   -> if s                      then Some (fst x.Current) else None
        type SomeValues   = SomeValues   with static member ($) (SomeValues  , x:IEnumerator<_>) = fun s p -> if s && p (fst x.Current) then Some (snd x.Current) else None
        type MoveNextIf   = MoveNextIf   with static member ($) (MoveNextIf  , x:IEnumerator<_>) = fun s p -> if s && p (fst x.Current) then (x.MoveNext())       else s

    open Tuple
    open EnumeratorFunctionAsTypes
    open PairSeqInternalValues
    
    let inline innerZip sortedKeyValueSeqs = seq {
        let enums = sortedKeyValueSeqs |> mapType GetEnumerator
        let status = ref (mapType MoveNext enums)
        while (!status |> Tuple.toList |> List.forall id) do
            let currentKeys = enums |> mapType CurrentKey |> Tuple.toList
            let currentKey = List.minBy id currentKeys
            if List.forall ( (=) currentKey) currentKeys then yield currentKey , mapType CurrentValue enums
            status := applySingle (mapType MoveNextIf enums <*> !status) ((=) currentKey)
        mapType DisposeEnum enums |> ignore
        }

    let inline outerZip sortedKeyValueSeqs = seq {
        let enums = sortedKeyValueSeqs |> mapType GetEnumerator
        let status = ref (mapType MoveNext enums)
        while (!status |> Tuple.toList |> List.exists id) do
            let currentKeys = enums |> mapType SomeKeys <*> status.Value |> Tuple.toList
            let currentKey = currentKeys |> List.choose id |> List.minBy id
            yield currentKey, applySingle (mapType SomeValues enums <*> !status) ((=) currentKey)
            status :=         applySingle (mapType MoveNextIf enums <*> !status) ((=) currentKey)
        mapType DisposeEnum enums |> ignore
        }